package com.whwj.heaven.auth.config;


import com.whwj.heaven.auth.exception.HeavenWebResponseExceptionTranslator;
import com.whwj.heaven.auth.service.impl.HeavenClientDetailsService;
import com.whwj.heaven.auth.service.impl.HeavenUserDetailsService;
import com.whwj.heaven.auth.service.impl.JwtTokenEnhancer;
import com.whwj.heaven.auth.service.impl.RedisAuthorizationCodeServices;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;

/**
 * 认证服务器配置
 *
 * @author dm
 * @date 2020/9/17
 */
@AllArgsConstructor
@Configuration
@EnableAuthorizationServer
public class Oauth2ServerConfig extends AuthorizationServerConfigurerAdapter {

    private final DataSource dataSource;
    private final TokenStore tokenStore;
    private final PasswordEncoder passwordEncoder;
    private final JwtTokenEnhancer jwtTokenEnhancer;
    private final AuthenticationManager authenticationManager;
    private final JwtAccessTokenConverter accessTokenConverter;
    private final HeavenUserDetailsService heavenUserDetailsService;
    private final RedisAuthorizationCodeServices redisAuthorizationCodeServices;
    private final HeavenWebResponseExceptionTranslator heavenWebResponseExceptionTranslator;


    @Override
    @SneakyThrows
    public void configure(ClientDetailsServiceConfigurer clients) {
        clients.withClientDetails(clientDetails());
    }
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
                .authenticationManager(authenticationManager)
                //授权码服务
                .authorizationCodeServices(authorizationCodeServices())
                //配置加载用户信息的服务
                .userDetailsService(heavenUserDetailsService);
                //令牌管理服务
                endpoints.tokenServices(tokenService());
                endpoints.setClientDetailsService(clientDetails());

        endpoints.exceptionTranslator(heavenWebResponseExceptionTranslator);

    }



    @Override
    public void configure(AuthorizationServerSecurityConfigurer security){
        security
                //oauth/token_key是公开
                .tokenKeyAccess("permitAll()")
                //oauth/check_token公开
                .checkTokenAccess("permitAll()")
                //表单认证（申请令牌）
                .allowFormAuthenticationForClients();
    }


    /**
     * 将客户端信息存储到数据库
     */
    @Bean
    public ClientDetailsService clientDetails() {
        HeavenClientDetailsService heavenClientDetailsService = new HeavenClientDetailsService(dataSource);
        heavenClientDetailsService.setPasswordEncoder(passwordEncoder);
        return heavenClientDetailsService;
    }


    @Bean
    public AuthorizationCodeServices authorizationCodeServices() {
        //设置授权码模式的授权码如何存取
        return redisAuthorizationCodeServices;
    }

//    /**
//     *  授权信息保存策略 没啥意义
//     *  当前保存至JDBC
//     *  存储表：oauth_approvals
//     *  endpoints.approvalStore(approvalStore());
//     */
//    @Bean
//    public ApprovalStore approvalStore() {
//        return new JdbcApprovalStore(dataSource);
//    }

    /**
     * 令牌管理服务 <p>注意，自定义TokenServices的时候，需要设置@Primary，否则报错，</p>
     * @return
     */
    @Primary
    @Bean
    public DefaultTokenServices tokenService(){
        DefaultTokenServices tokenServices = new DefaultTokenServices();
//        tokenServices.setClientDetailsService(clientDetails());
        //支持刷新令牌
        tokenServices.setSupportRefreshToken(true);
        tokenServices.setAuthenticationManager(authenticationManager);
        //令牌存储策略
        tokenServices.setTokenStore(tokenStore);
        //配置JWT的内容增强器
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> delegates = new ArrayList<>();
        delegates.add(jwtTokenEnhancer);
        delegates.add(accessTokenConverter);
        tokenEnhancerChain.setTokenEnhancers(delegates);
        tokenServices.setTokenEnhancer(tokenEnhancerChain);

        // token有效期自定义设置，默认12小时,太高了，改为1小时
        tokenServices.setAccessTokenValiditySeconds(60*60);
        // refresh_token默认30天，改为一星期
        tokenServices.setRefreshTokenValiditySeconds(60 * 60 * 24 * 7);
        return tokenServices;
    }





}
